﻿
namespace CmdArgs.Settings
{
    using System.Diagnostics;
    using System.Text;

    /// <summary>
    /// Provides static factory methods for <see cref="IOptionParametersCount"/> policies.
    /// </summary>
    public static class OptionParametersCount
    {
        /// <summary>
        /// The option must have at least given number of parameters.
        /// </summary>
        /// <returns>Instance of parameters count policy</returns>
        public static IOptionParametersCount AtLeast(int count)
        {
            return new AtLeastImpl(count);
        }

        /// <summary>
        /// The option must not have more than given number of parameters.
        /// </summary>
        /// <returns>Instance of parameters count policy</returns>
        public static IOptionParametersCount AtMost(int count)
        {
            if (count == 1)
            {
                return new ExactlyOneImpl();
            }
            else
            {
                return new AtMostImpl(count);
            }
        }

        /// <summary>
        /// The option must have exactly given number of parameters.
        /// </summary>
        /// <returns>Instance of parameters count policy</returns>
        public static IOptionParametersCount Exactly(int count)
        {
            if (count == 1)
            {
                return new ExactlyOneImpl();
            }
            else
            {
                return new ExactlyImpl(count);
            }
        }

        /// <summary>
        /// The option has no parameters.
        /// </summary>
        /// <returns>Instance of parameters count policy</returns>
        public static IOptionParametersCount NoParameters()
        {
            return new NoParametersImpl();
        }

        /// <summary>
        /// The option may have infinite number of parameters as well as no one.
        /// </summary>
        /// <returns>Instance of parameters count policy</returns>
        public static IOptionParametersCount Any()
        {
            return new AnyImpl();
        }

        private sealed class AtLeastImpl : IOptionParametersCount
        {
            private int count;

            public AtLeastImpl(int count)
            {
                this.count = count;
            }

            public string ErrorMessage
            {
                get { return string.Format("The option must have at least {0} parameters.", this.count); }
            }

            public bool Satisfies(int parameterIndex)
            {
                return true;    // All command line args after the options are it's parameters
            }

            public bool IsValidCount(int parametersCount)
            {
                return parametersCount >= this.count;   // At least the given count...
            }

            public string GetParametersListForHelp(string parameterName, bool forLongName)
            {
                var parametersList = new StringBuilder(" ");
                for (int i = 0; i < this.count; i++)
                {
                    parametersList.AppendFormat("{0}{1} ", parameterName, i);
                }

                return parametersList
                    .AppendFormat("[{0}{1} ...]", parameterName, this.count)
                    .ToString();
            }
        }

        private sealed class AtMostImpl : IOptionParametersCount
        {
            private int count;
            
            public AtMostImpl(int count)
	        {
                this.count = count;
	        }

            public string ErrorMessage
            {
                get 
                { 
                    Debug.Assert(
                        false, 
                        "This shouldn't happen because after parsing n parameters parser should recognize next arguments as ordinary arguments"); 
                    return string.Empty; 
                }
            }

            public bool Satisfies(int parameterIndex)
            {
                return parameterIndex < this.count;
            }

            public bool IsValidCount(int parametersCount)
            {
                return true;
            }

            public string GetParametersListForHelp(string parameterName, bool forLongName)
            {
                var parametersList = new StringBuilder(" ");
                parametersList.Append("[");
                for (int i = 0; i < this.count; i++)
                {
                    parametersList.AppendFormat("{0}{1} ", parameterName, i);
                }
                parametersList.Remove(parametersList.Length - 1, 1);    //remove last space
                parametersList.Append("]");

                return parametersList.ToString();
            }
        }

        private sealed class ExactlyImpl : IOptionParametersCount
        {
            private int count;
            
            public ExactlyImpl(int count)
	        {
                this.count = count;
	        }

            public string ErrorMessage
            {
                get { return string.Format("The option must have exactly {0} parameters.", this.count); }
            }


            public bool Satisfies(int parameterIndex)
            {
                return parameterIndex < this.count;
            }

            public bool IsValidCount(int parametersCount)
            {
                return parametersCount == count;
            }

            public string GetParametersListForHelp(string parameterName, bool forLongName)
            {
                var parametersList = new StringBuilder(" ");
                for (int i = 0; i < this.count; i++)
                {
                    parametersList.AppendFormat("{0}{1} ", parameterName, i);
                }

                return parametersList.ToString();
            }
        }

        private sealed class ExactlyOneImpl : IOptionParametersCount
        {           
            public string ErrorMessage
            {
                get { return string.Format("The option must have one parameter."); }
            }

            public bool Satisfies(int parameterIndex)
            {
                return parameterIndex == 0;
            }

            public bool IsValidCount(int parametersCount)
            {
                return parametersCount == 1;
            }

            public string GetParametersListForHelp(string parameterName, bool forLongName)
            {
                if (forLongName)
                {
                    return "=" + parameterName;
                }
                else
                {
                    return " " + parameterName;
                }
            }
        }

        private sealed class NoParametersImpl : IOptionParametersCount
        {
            public string ErrorMessage
            {
                get 
                {
                    Debug.Assert(false, "This parameters count policy cannot cause error.");
                    return string.Empty; 
                }
            }

            public bool Satisfies(int parameterIndex)
            {
                return false;
            }

            public bool IsValidCount(int parametersCount)
            {
                Debug.Assert(parametersCount == 0, "Since Satisfies always returns false, no parameters should be parsed");
                return true;
            }

            public string GetParametersListForHelp(string parameterName, bool forLongName)
            {
                return " ";
            }
        }

        private sealed class AnyImpl : IOptionParametersCount
        {
            public string ErrorMessage
            {
                get
                {
                    Debug.Assert(false, "This parameters count policy cannot cause error.");
                    return string.Empty;
                }
            }

            public bool Satisfies(int parameterIndex)
            {
                return true;
            }

            public bool IsValidCount(int parametersCount)
            {
                return true;
            }

            public string GetParametersListForHelp(string parameterName, bool forLongName)
            {
                return string.Format("[{0} ...]", parameterName);
            }
        }
    }
}
